/*
 * File Name: httpstuff.c
 *
 * Group Members: Cassie Chin (cassiechin9793@gmail.com)
 *                Stephen Smetana (stephensmetana11@gmail.com)
 *                Logan Widick (logan.widick@gmail.com)
 *
 * File Description:
 *
 *
 */
#define _GNU_SOURCE

#include "httpstuff.h" 
#include <regex.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <sys/select.h>

#include <assert.h>

#define DEFAULT_HTTP_PORT "80"
#define DEFAULT_FILE_NAME "/index.html"
#define MIN_BUFFERLEN 5
#define TIMEOUT_SEC 5
#define TIMEOUT_USEC 10
#define SEND_FLAGS 0

#define MIN(X,Y) ((X) < (Y) ? (X) : (Y))

#define RECV_FLAGS 0
/**
 * Contains the status code, the reason-phrase, and whether or not a body can be found.
 */
static HTTPResponseCode responseCodes[] = { { 100, "Continue", false }, { 101,
		"Switching Protocols", false }, { 200, "OK", false }, { 201, "Created",
		false }, { 202, "Accepted", false }, { 203,
		"Non-Authoritative Information", false }, { 204, "No Content", false },
		{ 205, "Reset Content", false }, { 206, "Partial Content", false }, {
				300, "Multiple Choices", true }, { 301, "Moved Permanently",
				true }, { 302, "Found", true }, { 303, "See Other", true }, {
				304, "Not Modified", false }, { 305, "Use Proxy", true }, { 306,
				"(Unused)", true }, { 307, "Temporary Redirect", true }, { 400,
				"Bad Request", true }, { 401, "Unauthorized", true }, { 402,
				"Payment Required", true }, { 403, "Forbidden", true }, { 404,
				"Not Found", true }, { 405, "Method Not Allowed", true }, { 406,
				"Not Acceptable", true }, { 407,
				"Proxy Authentication Required", true }, { 408,
				"Request Timeout", true }, { 409, "Conflict", true }, { 410,
				"Gone", true }, { 411, "Length Required", true }, { 412,
				"Precondition Failed", true }, { 413,
				"Request Entity Too Large", true }, { 414,
				"Request-URI Too Long", true }, { 415, "Unsupported Media Type",
				true }, { 416, "Requested Range Not Satisfiable", true }, { 417,
				"Expectation Failed", true }, { 500, "Internal Serval Error",
				true }, { 501, "Not Implemented", true }, { 502, "Bad Gateway",
				true }, { 503, "Service Unavailable", true }, { 504,
				"Gateway Timeout", true }, { 505, "HTTP Version Not Supported",
				true },

};

/**
 * Returns the reason-phrase for the code
 * @param code
 * @return a string. DO NOT FREE THIS!
 */
char* getReasonPhraseForCode(int code) {
	int maxSize = sizeof(responseCodes) / sizeof(HTTPResponseCode);
	int min = 0;
	int mid = maxSize / 2;
	int max = maxSize - 1;
	while (min <= max) {
		mid = max + mid / 2;
		if (responseCodes[mid].num == code) {
			return responseCodes[mid].desc;
		}
		//mid > code; go back
		else if (responseCodes[mid].num > code) {
			max = mid - 1;
		}
		//mid < code; go up
		else if (responseCodes[mid].num < code) {
			min = mid + 1;
		}
	}

	//if we are still here issue default code.
	//1xx Informational
	if (code / 100 == 1) {
		return "Informational Request Received Continuing";
	}
	//2xx success
	else if (code / 100 == 2) {
		return "Successful";
	}
	//3xx Redirect
	else if (code / 100 == 3) {
		return "Redirect";
	}
	//4xx client error
	else if (code / 100 == 4) {
		return "Client Error";
	}
	//5xx Server Error
	else if (code / 100 == 5) {
		return "Server Error";
	}
	//ok this should not happen.
	else {
		return NULL;
	}
}

/**
 * A convenience function for deciding if a message type can have a body.
 * @param code the status code of the message
 * @return true if the message can have a body or false otherwise.
 */
bool isBodyOk(int code) {
	int maxSize = sizeof(responseCodes) / sizeof(HTTPResponseCode);
	int min = 0;
	int mid = maxSize / 2;
	int max = maxSize - 1;
	while (min <= max) {
		mid = max + mid / 2;
		if (responseCodes[mid].num == code) {
			return responseCodes[mid].canHaveBody;
		}
		//mid > code; go back
		else if (responseCodes[mid].num > code) {
			max = mid - 1;
		}
		//mid < code; go up
		else if (responseCodes[mid].num < code) {
			min = mid + 1;
		}
	}

	//assume we can
	return true;
}

/******************************************************************************/
/*                         START HEADER STUFF                                 */
/******************************************************************************/
/**
 * Constructor
 * @return a new header list
 */
HeaderList* new_HeaderList() {
	HeaderList *self = calloc(1, sizeof(HeaderList));

	self->head = NULL;
	self->tail = NULL;

	return self;
}

/**
 * Destructor
 * @param list the header list to destroy
 */
void delete_HeaderList(HeaderList *list) {
	if (!list)
		return;
	HeaderListNode *nxt_iter = NULL;
	HeaderListNode *iter = list->head;

	while (iter != NULL) {
		nxt_iter = iter->next;
		if (iter->name)
			free(iter->name);
		if (iter->value)
			free(iter->value);
		if (iter)
			free(iter);
		iter = nxt_iter;
	}
	if (list)
		free(list);
}

/**
 * Adds a new header given the name and value
 * @param list
 * @param name
 * @param value
 */
void HeaderList_addHeader(HeaderList *list, char *name, char *value) {
	if (!list)
		return;

	// Allocate memory for the node and set values
	HeaderListNode *ptr2HeaderNode = (HeaderListNode*) calloc(1,
			sizeof(HeaderListNode));
	ptr2HeaderNode->name = strdup(name);
	ptr2HeaderNode->value = strdup(value);
	ptr2HeaderNode->next = NULL;

	if (list->tail) {
		// Set the current tail's next pointer, to point to the new node.
		list->tail->next = ptr2HeaderNode;
		// Set the tail of the list to the new node.
		list->tail = ptr2HeaderNode;
	} else {
		list->head = ptr2HeaderNode;
		list->tail = ptr2HeaderNode;
	}
}

/**
 * Determines if a header exists.
 * @param list the list to search through
 * @param name the name of the header to search for
 * @return true if the header exists; otherwise false
 */
bool HeaderList_doesHeaderExist(HeaderList *list, char *name) {
	if (!list) {
		return false;
	}
	HeaderListNode *iter = list->head;
	for (iter = list->head; iter != NULL; iter = iter->next) {
		if (!iter->name || !iter->value)
			continue;
		if (strcasecmp(name, iter->name) == 0) {
			return true;
		}
	}
	return false;
}

/**
 * Prints out the header list to a file
 * @param list the list to print out.
 * @param thefile the file to print it to.
 */
void HeaderList_print(HeaderList *list, FILE *theFile) {
	if (!list)
		return;
	HeaderListNode *iter = list->head;
	for (iter = list->head; iter != NULL; iter = iter->next) {
		printf("%s: %s\n", iter->name, iter->value);
	}
}
/**
 * Returns a pointer to the value for a header.
 * THIS METHOD DOES NOT COPY SO PLEASE DO NOT ALTER THE RETURNED VALUE. DO NOT FREE.
 * @param list the header list
 * @param name the name of the header.
 */
char *HeaderList_getValue(HeaderList *list, char *name) {
	if (!list) {
		return NULL;
	}
	HeaderListNode *iter = list->head;
	for (iter = list->head; iter != NULL; iter = iter->next) {
		if (!iter->name || !iter->value) {
			continue;
		} else if (strcasecmp(name, iter->name) == 0) {
			return iter->value;
		}
	}
	return NULL;
}

/**
 * Removes a header from a list by name
 * @param list the headerlist instance
 * @param key the name of the header to remove.
 */
void HeaderList_removeHeaderByKey(HeaderList *list, char *key) {
	if (!list || !key)
		return;

	HeaderListNode *curr_iter = NULL;
	HeaderListNode *prev_iter = NULL;
	for (curr_iter = list->head; curr_iter != NULL; curr_iter =
			curr_iter->next) {
		if (curr_iter->name && strcasecmp(curr_iter->name, key) == 0) {
			break;
		}
		prev_iter = curr_iter;
	}

	if (!curr_iter)
		return;

	//set any pointers
	if (curr_iter == list->head && curr_iter == list->tail) {
		list->head = NULL;
		list->tail = NULL;
	} else if (curr_iter == list->head) {
		list->head = list->head->next;
	} else if (curr_iter == list->tail && prev_iter) {
		prev_iter->next = NULL;
		list->tail = prev_iter;
	} else if (prev_iter && curr_iter) {
		prev_iter->next = curr_iter->next;
	} else {
		//should not happen
		assert(false);
	}

	//now free the curr_iter
	if (curr_iter->name)
		free(curr_iter->name);
	if (curr_iter->value)
		free(curr_iter->value);
	free(curr_iter);
}

/**
 * Appends a new value to the last header in the list.
 * @param list
 * @param value
 */
void HeaderList_addToLastHeader(HeaderList *list, char *value) {
	if (!list || !value)
		return;
	if (!list->tail)
		return;

	char *tmpbuf = (char *) calloc(
			strlen(value) + strlen(list->tail->value) + 2, sizeof(char));
	if (!tmpbuf)
		return;

	/*
	 * http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
	 * Multiple message-header fields with the same field-name MAY be present
	 * in a message if and only if the entire field-value for that header field
	 * is defined as a comma-separated list [i.e., #(values)].
	 * It MUST be possible to combine the multiple header fields into one
	 * "field-name: field-value" pair, without changing the semantics of the message,
	 * by appending each subsequent field-value to the first, each separated by a comma.
	 * The order in which header fields with the same field-name are received is therefore
	 * significant to the interpretation of the combined field value, and thus a proxy
	 * MUST NOT change the order of these field values when a message is forwarded.
	 */
	strcat(tmpbuf, list->tail->value);
	strcat(tmpbuf, ",");
	strcat(tmpbuf, value);

	//we have a new buffer, so out with the old
	if (list->tail->value)
		free(list->tail->value);

	//and in with the new
	list->tail->value = tmpbuf;
}

/******************************************************************************/
/*                         END HEADER STUFF                                   */
/******************************************************************************/

/*****************************************************************************/
/*                        BEGIN FILE PTR SET STUFF                           */
/*****************************************************************************/

/**
 * Opens a client connection given the port, ip, and a pointer to a FilePointerSet to be filled
 * @param PORT the port # as a string
 * @param IP the IP address to connect to
 * @param ptrSet the pointer to the FilePointerSet to fill.
 * @return 0 on success, something negative on fail.
 */
int openClientConnection(char *PORT, char *IP, FilePointerSet *ptrSet) {
	if (!PORT || !IP || !ptrSet)
		return -1;

	//Connection code adapted from 
	//http://beej.us/guide/bgnet/output/html/multipage/clientserver.html#simpleclient

	struct addrinfo hints, *servinfo, *p;
	int sockfd = 0;
	int rv = 0;

	memset(&hints, 0, sizeof hints);
	memset(ptrSet, 0, sizeof(FilePointerSet));

	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;

	if ((rv = getaddrinfo(IP, PORT, &hints, &servinfo)) != 0) {
		fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
		return -1;
	}

	// loop through all the results and connect to the first we can
	for (p = servinfo; p != NULL; p = p->ai_next) {
		if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol))
				== -1) {
			perror("client: socket");
			continue;
		}

		if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
			close(sockfd);
			perror("client: connect");
			continue;
		}

		break;
	}
	if (p == NULL) {
		fprintf(stderr, "client: failed to connect\n");
		return -2;
	}

	//now set the file.
	ptrSet->inFile = sockfd;
	ptrSet->outFile = sockfd;

	freeaddrinfo(servinfo);
	return 0;
}
/*****************************************************************************/
/*                        END   FILE PTR SET STUFF                           */
/*****************************************************************************/

/******************************************************************************/
/*                         START HTTP STUFF                                   */
/******************************************************************************/

/**
 * Constructor
 * @return
 */
HTTPMsg *new_HTTPMsg() {
	HTTPMsg *self = (HTTPMsg *) calloc(1, sizeof(HTTPMsg));
	if (!self)
		return NULL;

	//the other fields are set to null via calloc.
	self->headerList = new_HeaderList();
	self->trailerList = new_HeaderList();
	self->contentLength = CONTENT_LENGTH_NOT_SET;
	self->version = 1.0;
	return self;
}

/**
 * destructor
 * @param msg
 */
void delete_HTTPMsg(HTTPMsg *msg) {
	if (msg->method)
		free(msg->method);
	if (msg->headerList)
		delete_HeaderList(msg->headerList);
	if (msg->trailerList)
		delete_HeaderList(msg->trailerList);
	if (msg->resource)
		free(msg->resource);
	if (msg->fileptr)
		fclose(msg->fileptr);
	if (msg->port)
		free(msg->port);
	if (msg->path)
		free(msg->path);
	if (msg->host)
		free(msg->host);
	if (msg->reasonPhrase)
		free(msg->reasonPhrase);
	if (msg)
		free(msg);
}

/**
 * Determines if a character is hexadecimal
 * @param ch
 * @return true if hex, false if not.
 */
bool isHex(char ch) {
	if (ch >= '0' && ch <= '9')
		return true;
	else if (ch >= 'a' && ch <= 'f')
		return true;
	else if (ch >= 'A' && ch <= 'F')
		return true;
	else
		return false;
}

/**
 * Decodes a URL
 * @param url
 * @return A new char* for the decoded url.
 */
char *decode_url(char *url) {
	if (!url)
		return NULL;
	int len = strlen(url);
	char *retbuf = calloc(len + 1, sizeof(char));
	int i = 0;
	int j = 0;
	char hexbuf[3];
	int chars_after_perc = 0;
	for (i = 0; i < len; i++) {
		//we see percent char, but are still trying to decode the previous percent encoded. Error.
		if (url[i] == '%' && chars_after_perc != 0) {
			free(retbuf);
			return NULL;
		}
		//we see percent char; begin percent encode state
		else if (url[i] == '%') {
			chars_after_perc = 2;
		}
		//we see hex and expect chars after perc. Add to hexbuffer
		else if (isHex(url[i]) && chars_after_perc > 0) {
			hexbuf[2 - chars_after_perc] = url[i];
			chars_after_perc--;
			hexbuf[2] = '\0';

			//we have finished filling hexbuffer. Scan and store in retbuf
			if (chars_after_perc == 0) {
				unsigned int num = 0;
				sscanf(hexbuf, "%x", &num);
				retbuf[j] = (char) num;
				j++;
			}
		}

		//we see nonhex and expect hex. Error!
		else if (!isHex(url[i]) && chars_after_perc != 0) {
			free(retbuf);
			return NULL;
		}

		//we see a plus sign. Copy as space.
		else if (url[i] == '+') {
			retbuf[j] = ' ';
			j++;
		}

		//copy over and continue
		else {
			retbuf[j] = url[i];
			j++;
		}
	}
	return retbuf;
}

/**
 * Extracts the path portion of a URL.
 * Examples:
 * http://www.google.com/folder/file.html will obtain /folder/file.html
 * www.google.com/folder/file.html will obtain /folder/file.html
 * google.com:8080/folder/file.html will obtain /folder/file.html
 * @param msg
 * @param theresource
 * @return the path portion of the url.
 */
char* HTTPMsg_extractPathPortionOfURL(HTTPMsg *msg, char *theresource) {
	if (!theresource || !msg)
		return NULL;

	/*
	 * Explanation:
	 * Group 0: 		the whole thing
	 * SubGroup 1: 		name of url scheme with colon but no slashes
	 * SubGroup 2: 		name of url scheme with no colon or slashes
	 * SubGroup 3: 		the name of the website with two slashes in front of it
	 * SubGroup 4: 		the name of the website with no slashes in front
	 * SubGroup 5: 		Either the path to the resource
	 * 			starting from the root of the site (/)
	 * 			or the name of the website (no slashes)
	 *			and the path to the resource starting at the root
	 *			(THIS IS THE BIG ONE HERE!!!!)
	 * SubGroups 6-7:	mainly used for supporting special features
	 * 			like questionmarks and pound targets.
	 *
	 * This regex was taken from http://tools.ietf.org/html/rfc3986#page-50
	 * but modified so that it would actually compile and match stuff.
	 *
	 * For this function, only subgroup 5 is relevant.
	 */
	regex_t regex;
	regcomp(&regex, "^(([^.:/?#]+):)?(//([^/?#]*))?([^?#]*)([^#]*)?(#(.*))?",
			REG_EXTENDED);
	regmatch_t *matches = calloc(regex.re_nsub + 1, sizeof(regmatch_t));
	char *match_buffer = NULL;

	regex_t subregex;

	/*
	 * The subregex to weed out stuff that appears before the path
	 * (like google.com in google.com/folder)
	 * Again, only SubGroup 5 is important.
	 */
	regcomp(&subregex, "(([^/?#])*)?((/)*)(([^#?])*)?", REG_EXTENDED);
	regmatch_t *submatches = calloc(subregex.re_nsub, sizeof(regmatch_t));

	//regexec returns 0 if successful
	if (regexec(&regex, theresource, regex.re_nsub, matches, 0) == 0) {
		//copy into new string
		char *new_buffer = calloc(matches[5].rm_eo - matches[5].rm_so + 1,
				sizeof(char));
		strncpy(new_buffer, &(theresource[matches[5].rm_so]),
				matches[5].rm_eo - matches[5].rm_so);

		//now apply the subregex
		if (regexec(&subregex, new_buffer, subregex.re_nsub, submatches, 0)
				== 0) {
			//copy into match buffer
			match_buffer = calloc(submatches[5].rm_eo - submatches[5].rm_so + 5,
					sizeof(char));
			strcpy(match_buffer, "/");
			strncat(match_buffer, &(new_buffer[submatches[5].rm_so]),
					(submatches[5].rm_eo - submatches[5].rm_so));

		}

		//handle url decode
		char *ret_buffer = decode_url(match_buffer);
		if (!ret_buffer) {
			free(match_buffer);
			match_buffer = NULL;
		} else {
			free(match_buffer);
			match_buffer = ret_buffer;
		}

		if (new_buffer)
			free(new_buffer);
	}

	//cleanup
	regfree(&regex);
	if (matches)
		free(matches);
	regfree(&subregex);
	if (submatches)
		free(submatches);

	//exit
	return (match_buffer);
}

/**
 * Determines if the http message is text.
 * @param msg the message
 * @return true if it should be filtered; otherwise false
 */
bool HTTPMsg_shouldFilter(HTTPMsg *msg) {
	//return false;
	if (!msg || !msg->headerList || !msg->fileptr)
		return false;
	char *val = HeaderList_getValue(msg->headerList, "Content-Type");

	//no content-type: assume text
	if (!val)
		return false;

	//content type not text; assume something that should not be filtered.
	else if (strcasestr(val, "text/") == NULL)
		return false;

	else
		return true;
}

/**
 * Gets the target info from the first folder in a URL.
 * Examples:
 * /www.example.com/file.html will set host=www.example.com, port=80
 * /www.example.com:8080/file.html will set host=www.example.com, port=8080
 * @param msg the HTTP message.
 * @param url the url.
 * @return
 * 		A pointer to the string starting AFTER the first folder. DO NOT FREE.
 * 		The two above examples will both return "/file.html"
 */
char* HTTPMsg_getTargetInfoFromFirstFolderInURL(HTTPMsg *msg, char *url) {

	//fprintf(stderr, "getTargetInfoFromFirstFolder called with %s\n", url);
	int firstSlashInd = 0;
	int nextSlashInd = -1;
	int i = 0;
	int len = 0;
	int endingColonInd = -1;

	if (!msg || !url)
		return NULL;

	if (url[0] != '/')
		firstSlashInd = -1;

	//now try to find next slash.
	len = strlen(url);
	for (i = firstSlashInd + 1; i < len; i++) {
		if (url[i] == '/') {
			nextSlashInd = i;
			break;
		}
	}

	//if we do not have a next slash, then the next slash is the end of this string. 
	if (nextSlashInd == -1)
		nextSlashInd = len;

	//find the ending colon
	for (i = nextSlashInd - 1; i > firstSlashInd; i--) {
		if (url[i] == ':') {
			endingColonInd = i;
			break;
		}
	}

	//no ending colon; port = default; host = the whole substring
	if (endingColonInd == -1) {
		msg->host = calloc(nextSlashInd - firstSlashInd + 2, sizeof(char));
		memmove(msg->host, &(url[firstSlashInd + 1]),
				(nextSlashInd - (firstSlashInd + 1)) * sizeof(char));

		msg->port = strdup(DEFAULT_HTTP_PORT);
		//fprintf(stderr, "msg->host %s, msg->port %s\n", msg->host, msg->port);
	}

	//ending colon; port=what appears after colon. host=what appears before colon
	else {
		msg->host = calloc(endingColonInd - firstSlashInd + 2, sizeof(char));
		memmove(msg->host, &(url[firstSlashInd + 1]),
				(endingColonInd - (firstSlashInd + 1)) * sizeof(char));

		msg->port = calloc(nextSlashInd - endingColonInd + 2, sizeof(char));
		memmove(msg->port, &(url[endingColonInd + 1]),
				(nextSlashInd - (endingColonInd + 1)) * sizeof(char));
		//fprintf(stderr, "msg->host %s, msg->port %s\n", msg->host, msg->port);
	}

	return &(url[nextSlashInd]);
}

/**
 * Extracts the portion of the URL for the host and port.
 * Examples:
 * http://www.google.com/file.html will return a string starting with "www.google.com"
 * http://www.google.com:8080/file.html will return a string starting with "www.google.com:8080"
 * www.google.com:80/file.html will return a string starting with "www.google.com:80"
 * www.google.com:80/file.html
 * @param msg
 * @param theresource
 * @return a new string for the portion of the URL that contains the host and port.
 */
char* HTTPMsg_extractHostPortPortionOfURL(HTTPMsg *msg, char *theresource) {
	assert(theresource);
	assert(msg);
	/*
	 * Explanation:
	 * Group 0: the whole thing
	 * SubGroup 1: 		name of url scheme with colon but no slashes
	 * SubGroup 2: 		name of url scheme with no colon or slashes
	 * SubGroup 3: 		the name of the website with two slashes in front of it
	 * SubGroup 4: 		the name of the website with no slashes in front
	 * SubGroup 5: 		Either the path to the resource 
	 *			starting from the root of the site (/)
	 * 			or the name of the website (no slashes)
	 *			and the path to the resource starting at the root
	 * SubGroups 6-7:	mainly used for supporting special features 
	 *			like questionmarks and pound targets.
	 *
	 * This regex was taken from http://tools.ietf.org/html/rfc3986#page-50
	 * but modified so that it would actually compile and match stuff.
	 *
	 * For this function, try to use subgroup 4.
	 * If subgroup 4 is not present, return subgroup 5.
	 */
	regex_t regex;
	regcomp(&regex, "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)([^#]*)?(#(.*))?",
			REG_EXTENDED);
	regmatch_t *matches = calloc(regex.re_nsub + 1, sizeof(regmatch_t));
	char *match_buffer = NULL;

	//regexec returns 0 if successful
	if (regexec(&regex, theresource, regex.re_nsub, matches, 0) == 0) {
		//no difference in place means that match did not exist
		if (matches[4].rm_eo - matches[4].rm_so == 0) {
			//fprintf(stderr, "Match[4] not found\n");
			//if grp 4 is not present, use grp 5.
			if (matches[5].rm_eo - matches[5].rm_so > 0) {
				//fprintf(stderr, "Match[5] found\n");
				match_buffer = calloc(matches[5].rm_eo - matches[5].rm_so + 1,
						sizeof(char));
				assert(match_buffer);
				memmove(match_buffer, &(theresource[matches[5].rm_so]),
						(matches[5].rm_eo - matches[5].rm_so) * sizeof(char));
			} else {
				assert(false);
			}
		}
		//group 4 is present; use it!
		else if (matches[4].rm_eo - matches[4].rm_so > 0) {
			//fprintf(stderr, "Match[4] found\n");
			match_buffer = calloc(matches[4].rm_eo - matches[4].rm_so + 1,
					sizeof(char));
			assert(match_buffer);
			memmove(match_buffer, &(theresource[matches[4].rm_so]),
					(matches[4].rm_eo - matches[4].rm_so) * sizeof(char));
		} else
			assert(false);
	} else {
		assert(false);
	}

	//cleanup
	regfree(&regex);
	if (matches)
		free(matches);

	//exit
	return match_buffer;
}

/**
 * Sets the HTTPMsg to use a given URL for making the host, port, path, etc.
 * @param msg the HTTPMsg.
 * @param theresource the URL to use.
 */
void HTTPMsg_useResource(HTTPMsg *msg, char *theresource,
		bool useFirstFolderAsLoc) {
	if (!msg || !theresource)
		return;

	//we already set the info; exit.
	if (msg->port)
		return;

	char *pathPortion = NULL;
	char *resourcePortion = NULL;

	if (useFirstFolderAsLoc) {
		/*
		 * we are to use the
		 * proxy:proxy_port/dest:dest_port/path_on_dest
		 * convention.
		 * This means that the dest:dest_port (the port and host to use)
		 * are buried within the path (which contains all but proxy:proxy_port)
		 */
		//get the proxy path
		pathPortion = HTTPMsg_extractPathPortionOfURL(msg, theresource);
		//fprintf(stderr, "useFirstFolderAsLoc pathPortion %s\n", pathPortion);

		//use the first folder in the proxy path as the host/port combo
		char *pathPortion2 = HTTPMsg_getTargetInfoFromFirstFolderInURL(msg,
				pathPortion);
		//fprintf(stderr, "useFirstFolderAsLoc pathPortion2 %s\n", pathPortion2);
		//copy into a new string
		msg->path = strdup(pathPortion2);

		//clean up.
		free(pathPortion);
	} else {

		//fprintf(stderr, "useResource start: %s\n", theresource);
		/*
		 * Regular convention: get resource and path separately
		 */
		//get the resource portion
		resourcePortion = HTTPMsg_extractHostPortPortionOfURL(msg, theresource);
		assert(resourcePortion);
		int i = strlen(resourcePortion) - 1;
		int lastColonInd = -1;
		for (; i >= 0; i--) {
			if (resourcePortion[i] == ':') {
				lastColonInd = i;
				break;
			}
		}
		if (lastColonInd < 0) {
			msg->host = strdup(resourcePortion);
			msg->port = strdup(DEFAULT_HTTP_PORT);
		} else {
			resourcePortion[lastColonInd] = '\0';
			msg->host = strdup(resourcePortion);
			msg->port = strdup(&(resourcePortion[lastColonInd + 1]));
		}
		//fprintf(stderr, "useResource resourcePortion: %s, msg->port %s, msg->host %s\n", resourcePortion, msg->port, msg->host);

		//get the portion of the url containing the path for the resource
		pathPortion = HTTPMsg_extractPathPortionOfURL(msg, theresource);
		//fprintf(stderr, "useResource pathPortion: %s\n", pathPortion);

		//copy into a new string
		msg->path = strdup(pathPortion);

		//cleanup
		free(resourcePortion);
		free(pathPortion);
	}
}

/**
 * Use the "Host" header to set the host and port info.
 * @param msg the HTTPMsg.
 */
void HTTPMsg_useHost(HTTPMsg *msg) {
	if (!msg)
		return;

	//we have already set the port; exit.
	if (msg->port)
		return;

	//get the Host header.
	char *headerVal = HeaderList_getValue(msg->headerList, "Host");
	if (!headerVal)
		return;

	//this should get the entire host string.
	char *resourcePortion = HTTPMsg_extractHostPortPortionOfURL(msg, headerVal);

	//use the value of the resource portion.
	HTTPMsg_getTargetInfoFromFirstFolderInURL(msg, resourcePortion);

	//For the path, use the resource specified in the request-uri for the get
	char *pathPortion = HTTPMsg_extractPathPortionOfURL(msg, msg->resource);
	msg->path = strdup(pathPortion);

	//clean up
	free(pathPortion);
	free(resourcePortion);
}

/**
 * Tries every way to load the host, path, and port info for a message.
 * @param msg
 * @param useFirstFolderAsLoc if true, this is the proxy
 */
void HTTPMsg_loadHostPathAndPort(HTTPMsg *msg, bool useFirstFolderAsLoc) {
	if (useFirstFolderAsLoc) {
		/* If we are to follow the
		 * proxy:proxy_port/dest_host:dest_port/path_on_dest
		 * convention, then all we can use is the GET request-uri.
		 */
		HTTPMsg_useResource(msg, msg->resource, true);
	} else {
		/*
		 * If we are not to follow that convention, use the following order of precedence:
		 * Location
		 * Content-Location
		 * Host
		 * The Request-URI in the GET
		 */
		HTTPMsg_useResource(msg,
				HeaderList_getValue(msg->headerList, "Location"), false);
		if (msg->path)
			return;

		HTTPMsg_useResource(msg,
				HeaderList_getValue(msg->headerList, "Content-Location"),
				false);
		if (msg->path)
			return;

		HTTPMsg_useHost(msg);
		if (msg->path)
			return; //check if we have obtained the path.

		HTTPMsg_useResource(msg, msg->resource, false);
	}
}

/**
 * Returns true if the URL contains an argument section.
 * If the URL contains an argument section, we probably should not cache it. 
 * @param msg the message
 * @param theresource the URL to test.
 * @return true if an argument section is present; otherwise false. 
 */
bool HTTPMsg_doesResourceContainArgSection(HTTPMsg *msg, char *theresource) {
	bool toReturn = false;
	if (!theresource || !msg)
		return false;
	/*
	 * Explanation:
	 * Group 0: the whole thing
	 * SubGroup 1: 		name of url scheme with colon but no slashes
	 * SubGroup 2: 		name of url scheme with no colon or slashes
	 * SubGroup 3: 		the name of the website with two slashes in front of it
	 * SubGroup 4: 		the name of the website with no slashes in front
	 * SubGroup 5: 		Either the path to the resource starting from the root of the site (/)
	 * 			or the name of the website (no slashes)
	 *			and the path to the resource starting at the root
	 * SubGroups 6-7: 	mainly used for supporting special features 
	 *			like questionmarks and pound targets.
	 *
	 * This regex was taken from http://tools.ietf.org/html/rfc3986#page-50
	 * but modified so that it would actually compile and match stuff.
	 *
	 * For this function, check that subgroup 6 is present. 
	 */
	regex_t regex;
	regcomp(&regex, "^(([^.:/?#]+):)?(//([^/?#]*))?([^?#]*)([^#]*)?(#(.*))?",
			REG_EXTENDED);
	regmatch_t *matches = calloc(regex.re_nsub + 1, sizeof(regmatch_t));

	//regexec returns 0 if successful
	if (regexec(&regex, theresource, regex.re_nsub, matches, 0) == 0) {
		//no difference in place means that match did not exist
		if (matches[6].rm_eo - matches[6].rm_so == 0) {
			toReturn = false;
		}
		//group 6 is present; use it!
		else {
			toReturn = true;
		}
	}

	//cleanup
	regfree(&regex);
	if (matches)
		free(matches);

	return toReturn;
}

/**
 * Attempts to guess if the message is dynamic. 
 * It is assumed that a URL is not dynamic unless it contains an argument section. 
 * @param msg the HTTPMsg. 
 */
bool HTTPMsg_isDynamic(HTTPMsg *msg, bool useFirstFolderAsLoc) {
	if (msg->method && strcasecmp(msg->method, "get") != 0)
		return true;
	char *cacheControl = HeaderList_getValue(msg->headerList, "cache-control");
	if (cacheControl && strcasestr(cacheControl, "no-cache"))
		return true;
	if (cacheControl && strcasestr(cacheControl, "no-store"))
		return true;
	if (cacheControl && strcasestr(cacheControl, "must-revalidate"))
		return true;
	if (cacheControl && strcasestr(cacheControl, "no-transform"))
		return true;

	if (useFirstFolderAsLoc) {
		/* If we are to follow the
		 * proxy:proxy_port/dest_host:dest_port/path_on_dest
		 * convention, then all we can use is the GET request-uri.
		 */
		if (HTTPMsg_doesResourceContainArgSection(msg, msg->resource))
			return true;
		else
			return false;
	} else {
		/*
		 * If we are not to follow that convention, use the following order of precedence:
		 * Content-Location
		 * The Request-URI in the GET
		 */
		if (HTTPMsg_doesResourceContainArgSection(msg,
				HeaderList_getValue(msg->headerList, "Content-Location")))
			return true;
		else if (HTTPMsg_doesResourceContainArgSection(msg,
				HeaderList_getValue(msg->headerList, "Location")))
			return true;
		else if (HTTPMsg_doesResourceContainArgSection(msg, msg->resource))
			return true;
		else
			return false;
	}

}

void HTTPMsg_print(HTTPMsg *msg, FILE* theFile) {
	(void) (theFile);
	if (!msg)
		return;
	fprintf(theFile, "-------- MESSAGE DUMP BEGIN ------------ \n");
	fprintf(theFile, "IsRequest: %d\n", msg->isRequest);
	if (msg->method)
		fprintf(theFile, "MethodInStruct: %s\n", msg->method);
	if (msg->resource)
		fprintf(theFile, "ResourceInStruct: %s\n", msg->resource);
	if (msg->port)
		fprintf(theFile, "PortInStruct: %s\n", msg->port);
	if (msg->path)
		fprintf(theFile, "PathInStruct: %s\n", msg->path);
	if (msg->host)
		fprintf(theFile, "HostInStruct: %s\n", msg->host);
	if (msg->reasonPhrase)
		fprintf(theFile, "ReasonPhraseInStruct: %s\n", msg->reasonPhrase);
	fprintf(theFile, "ContentLengthInStruct: %d\n", msg->contentLength);
	fprintf(theFile, "StatusCodeInStruct: %d\n", msg->statusCode);
	fprintf(theFile, "VersionInStruct: %f\n", msg->version);

	HeaderList_print(msg->headerList, theFile);
	fprintf(theFile, "--------- MESSAGE DUMP END ---------------\n");
}

/**
 * Extracts the path to the file found in the message's resource. This is mainly used by proxies. 
 * @param msg the message
 * @return
 * 		the path to the file on the server
 * 		(assuming that root is the current directory),
 * 		or NULL if the url is dynamic or cannot be parsed.
 */
char* HTTPMsg_findFilePath(HTTPMsg *msg, bool useFirstFolderAsLoc) {
	if (!msg)
		return NULL;

	//first, ensure that the host, path, port are good.
	HTTPMsg_loadHostPathAndPort(msg, useFirstFolderAsLoc);

	//if we still do not have a port # then the url is no good. 
	if (!msg->port)
		return NULL;

	//next, check that the URL is not dynamic.
	if (HTTPMsg_isDynamic(msg, useFirstFolderAsLoc))
		return NULL;

	//then, create new string.
	int len = strlen(msg->host) + strlen(msg->path) + strlen(DEFAULT_FILE_NAME)
			+ 8;
	char *retStr = calloc(len, sizeof(char));
	if (!retStr)
		return NULL;

	//copy into new string
	strcpy(retStr, "./");
	strcat(retStr, msg->host);
	strcat(retStr, msg->path);

	//check if the last item represents a file.
	int i = strlen(msg->path) - 1;
	int slashIndex = -1;
	int dotIndex = -1;
	for (; i >= 0; i--) {
		//break on first slash.
		if (msg->path[i] == '/') {
			slashIndex = i;
			break;
		} else if (msg->path[i] == '.') {
			dotIndex = i;
		}
	}

	if(dotIndex == -1) strcat(retStr, DEFAULT_FILE_NAME);
	return retStr;
}

/**
 * Prepends a string to a message's resource.
 * @param msg the httpmsg instance
 * @param strToPrepend the string to prepend
 */
void HTTPMsg_prependStringToResource(HTTPMsg *msg, char *strToPrepend) {
	if (!msg || !strToPrepend)
		return;
	//fprintf(stderr, "prepending path\n");
	if (msg->host) {
		char *newPath = calloc(strlen(msg->host) + strlen(strToPrepend) + 2,
				sizeof(char));
		strcpy(newPath, strToPrepend);
		strcpy(newPath, "/");
		strcpy(newPath, msg->host);
		free(msg->host);
		msg->host = newPath;
	}
	if (msg->path && !msg->host) {
		char *newPath = calloc(strlen(msg->path) + strlen(strToPrepend) + 2,
				sizeof(char));
		strcpy(newPath, strToPrepend);
		strcpy(newPath, "/");
		strcpy(newPath, msg->path);
		free(msg->path);
		msg->path = newPath;
	}
	//fprintf(stderr, "prepending resource\n");
	if (msg->resource) {
		char *newPath = calloc(strlen(msg->resource) + strlen(strToPrepend) + 2,
				sizeof(char));
		strcpy(newPath, strToPrepend);
		strcpy(newPath, "/");
		strcpy(newPath, msg->resource);
		free(msg->resource);
		msg->resource = newPath;
	}
}

/**
 * Convenience function for obtaining the resource represented by the message. 
 * @param msg the HTTPMsg
 * @return a new char* containing the resource. 
 */
char* HTTPMsg_getResource(HTTPMsg *msg) {
	if (!msg)
		return NULL;

	//first, try to use path & host.
	//if(msg->path){
	//	return strdup(msg->path);
	//}
	/*else*/if (msg->resource) {
		return strdup(msg->resource);
	} else {
		return NULL;
	}
}

/**
 * A method for sending everything in a buffer.
 * Adapted from http://beej.us/guide/bgnet/output/html/multipage/advanced.html#sendall. 
 * We modified it to change the return values and input parameters and to increase readability. 
 * @param fd the file descriptor
 * @param buffer the buffer to send from
 * @param len the length of the buffer
 * @param flags the flags to pass to send()
 * @return the number of bytes sent.
 */
int sendall(int fd, char *buffer, int len, int flags) {
	int bytes_sent = 0;
	int send_res = 0;
	while (bytes_sent < len) {
		send_res = send(fd, &(buffer[bytes_sent]), len - bytes_sent, flags);
		if (send_res <= 0)
			return bytes_sent;
		else
			bytes_sent += send_res;
	}
	return bytes_sent;
}

/**
 * Sends an HTTPMsg.
 * @param msg
 * 		The message to send out. Must be preset with the content-length,
 * 		file* (with the data; if applicable),
 * 		and whether or not it is a request or a response.
 * 		All fields applicable to the type of message must also be set.
 * 		The headers must also be present
 * 		(except content-length which can be END automatically).
 * 		May attempt to send transfer-encoding if not present.
 *
 * @param sock_out_fd the FILE* for writing to the socket.
 * @return 0 on success, or a negative error if FILE* is closed prematurely.
 */
int HTTPMsg_sendMsg(HTTPMsg *msg, int sock_out_fd) {
	if (!msg || !sock_out_fd)
		return -2;

	//use this line after implementing getReasonPhraseForCode
	char *reasonPhrase = NULL;

	//convert version to string
	char numbuf[NUMBER_BUFFER_SIZE + 1];
	char numbuf2[NUMBER_BUFFER_SIZE + 1];

	char *resource = HTTPMsg_getResource(msg);
	assert(!msg->isRequest || resource);
	snprintf(numbuf, NUMBER_BUFFER_SIZE, "HTTP/%.1f", msg->version);

	//strings for spaces and newlines
	char sp[] = " ";
	char newline[] = "\r\n";
	char colon_space[] = ": ";
	char content_length[] = "Content-Length";
	char xfer_encoding[] = "Transfer-Encoding";
	char chunked[] = "Chunked";

	//print out first line
	if (msg->isRequest) {
		//request format: method SP resource SP HTTP/version
		//// 
		/*
		 fwrite(msg->method, sizeof(char), strlen(msg->method), sock_out_fd);
		 fwrite(sp, sizeof(char), strlen(sp), sock_out_fd);
		 fwrite(resource, sizeof(char), strlen(resource),
		 sock_out_fd);
		 fwrite(sp, sizeof(char), strlen(sp), sock_out_fd);
		 fwrite(numbuf, sizeof(char), strlen(numbuf), sock_out_fd);
		 fwrite(newline, sizeof(char), strlen(newline), sock_out_fd);
		 */
		sendall(sock_out_fd, msg->method, strlen(msg->method) * sizeof(char),
				0);
		sendall(sock_out_fd, sp, strlen(sp) * sizeof(char), 0);
		sendall(sock_out_fd, resource, strlen(resource) * sizeof(char), 0);
		sendall(sock_out_fd, sp, strlen(sp) * sizeof(char), 0);
		sendall(sock_out_fd, numbuf, strlen(numbuf) * sizeof(char), 0);
		sendall(sock_out_fd, newline, strlen(newline) * sizeof(char), 0);
		//fprintf(stderr, "Requesting resource %s\n", resource);
	}

	//WE ARE DOING RESPONSE HERE
	else {
		reasonPhrase = msg->reasonPhrase;
		if(!reasonPhrase) reasonPhrase = getReasonPhraseForCode(msg->statusCode);

		//get status code
		snprintf(numbuf2, NUMBER_BUFFER_SIZE, "%d", msg->statusCode);

		//output format: HTTP/version SP status-code SP reason-phrase
		////
		/*
		 fwrite(numbuf, sizeof(char), strlen(numbuf), sock_out_fd);
		 fwrite(sp, strlen(sp), 1, sock_out_fd);
		 fwrite(numbuf2, sizeof(char), strlen(numbuf2), sock_out_fd);
		 fwrite(sp, sizeof(char), strlen(sp), sock_out_fd);
		 fwrite(reasonPhrase, sizeof(char), strlen(reasonPhrase),
		 sock_out_fd);
		 fwrite(newline, sizeof(char), strlen(newline), sock_out_fd);
		 */
		sendall(sock_out_fd, numbuf, strlen(numbuf) * sizeof(char), SEND_FLAGS);
		sendall(sock_out_fd, sp, strlen(sp) * sizeof(char), SEND_FLAGS);
		sendall(sock_out_fd, numbuf2, strlen(numbuf2) * sizeof(char),
				SEND_FLAGS);
		sendall(sock_out_fd, sp, strlen(sp) * sizeof(char), SEND_FLAGS);
		sendall(sock_out_fd, reasonPhrase, strlen(reasonPhrase) * sizeof(char),
				SEND_FLAGS);
		sendall(sock_out_fd, newline, strlen(newline) * sizeof(char),
				SEND_FLAGS);

		//DO NOT FREE reason phrase.
	}

	//print out headers
	HeaderListNode *iter = NULL;
	if (msg->headerList) {
		for (iter = msg->headerList->head; iter != NULL; iter = iter->next) {
			//format: Name: Value
			//// 
			sendall(sock_out_fd, iter->name, sizeof(char) * strlen(iter->name),
					SEND_FLAGS);
			sendall(sock_out_fd, colon_space,
					sizeof(char) * strlen(colon_space), SEND_FLAGS);
			sendall(sock_out_fd, iter->value,
					sizeof(char) * strlen(iter->value), SEND_FLAGS);
			sendall(sock_out_fd, newline, sizeof(char) * strlen(newline),
					SEND_FLAGS);
		}
	}

	//if we have a content-length print it out
	if (msg->contentLength > 0) {

		sendall(sock_out_fd, content_length,
				sizeof(char) * strlen(content_length), SEND_FLAGS);
		sendall(sock_out_fd, colon_space, sizeof(char) * strlen(colon_space),
				SEND_FLAGS);

		//use the buffer.
		snprintf(numbuf, NUMBER_BUFFER_SIZE - 1, "%d", msg->contentLength);

		//write to file
		sendall(sock_out_fd, numbuf, sizeof(char) * strlen(numbuf), SEND_FLAGS);
		sendall(sock_out_fd, newline, sizeof(char) * strlen(newline),
				SEND_FLAGS);
	}
	//write transfer encoding header unless already there
	else if (msg->contentLength == CHUNKED_LENGTH) {
		if (HeaderList_doesHeaderExist(msg->headerList, xfer_encoding)) {
			////
			sendall(sock_out_fd, xfer_encoding,
					sizeof(char) * strlen(xfer_encoding), SEND_FLAGS);
			sendall(sock_out_fd, colon_space,
					sizeof(char) * strlen(colon_space), SEND_FLAGS);
			sendall(sock_out_fd, chunked, sizeof(char) * strlen(chunked),
					SEND_FLAGS);
			sendall(sock_out_fd, newline, sizeof(char) * strlen(newline),
					SEND_FLAGS);
		}
	}

	//print out blank line
	////
	sendall(sock_out_fd, newline, strlen(newline) * sizeof(char), SEND_FLAGS);

	//print out body (should be the FILE* in the message)
	char buffer[CHUNK_SIZE + 1];
	int numBytes = 0;
	if (msg->fileptr) {
		rewind(msg->fileptr);
		if (msg->contentLength == CHUNKED_LENGTH) {
			//go through the file, sending CHUNK_SIZE bytes each time.
			while (!feof(msg->fileptr)) {
				numBytes = fread(buffer, sizeof(char), CHUNK_SIZE,
						msg->fileptr);
				if (numBytes <= 0)
					break;

				//chunk len
				snprintf(numbuf, NUMBER_BUFFER_SIZE, "%x\r\n",
						(unsigned int) numBytes);
				////
				sendall(sock_out_fd, numbuf, sizeof(char) * strlen(numbuf),
						SEND_FLAGS);

				//chunk data
				sendall(sock_out_fd, buffer, sizeof(char) * numBytes,
						SEND_FLAGS);

				//newline after chunk
				sendall(sock_out_fd, newline, sizeof(char) * strlen(newline),
						SEND_FLAGS);
			}

			//now for the terminating 0 chunk
			//chunk len
			snprintf(numbuf, NUMBER_BUFFER_SIZE, "0\r\n");
			////
			sendall(sock_out_fd, numbuf, sizeof(char) * strlen(numbuf),
					SEND_FLAGS);
		}

		//we are not using chunks
		else {
			//we are not using chunks; just send the entire file.
			while (!feof(msg->fileptr)) {
				numBytes = fread(buffer, sizeof(char), CHUNK_SIZE,
						msg->fileptr);
				if (numBytes <= 0)
					break;
				////
				sendall(sock_out_fd, buffer, sizeof(char) * numBytes,
						SEND_FLAGS);
			}
		}
	}

	free(resource);
	////
	sendall(sock_out_fd, newline, sizeof(char) * strlen(newline), SEND_FLAGS);

	return 0;
}

/**
 * Waits for a socket to become available.
 * @param sock_fd_in
 * @return
 * 		true if the socket has stuff in it,
 * 		or false if the socket timed out or closed.
 */
bool doSelecting(int sock_fd_in) {

	//initialize the timeout structure	
	struct timeval tv;
	tv.tv_sec = TIMEOUT_SEC;
	tv.tv_usec = TIMEOUT_USEC;
	fd_set in_fds;

	//initialize the set to just the sock_fd_in's descriptor.
	FD_ZERO(&in_fds);
	FD_SET(sock_fd_in, &in_fds);

	//select() should return the fd ready or zero.
	int selcode = select(FD_SETSIZE, &in_fds, NULL, NULL, &tv);

	if (selcode <= 0)
		return false;
	else
		return true;
}

/**
 * A version of fgets designed for lower-level file descriptors instead of FILE*.
 * Adapted from: http://man7.org/tlpi/code/online/dist/sockets/read_line.c.html
 * We modified it so that it would return NULL instead of 0 or -1,
 * and would return a pointer to the start of the buffer otherwise.
 * This modification was to make it an almost drop-in replacement for fgets,
 * which we were using previously.
 * @param buffer
 * @param buffer_size
 * @param fd the file descriptor.
 * @return
 * 		A pointer to the start of the buffer,
 * 		or NULL if stuff could not be read into the buffer.
 */
char* custom_fgets(char *buffer, int buffer_size, int fd) {

	if (!buffer || buffer_size <= 0)
		return NULL;
	char singleByte = 0;
	int numRead = 0;
	int total = 0;

	//this automatically ensures that things end in '\0'
	memset(buffer, 0, buffer_size * sizeof(char));

	while (total < buffer_size - 1) {
		numRead = recv(fd, &singleByte, 1 * sizeof(char), RECV_FLAGS);

		if (numRead == -1) {
			if (errno == EINTR) {
				/* Interrupted --> restart read() */
				continue;
			} else {
				perror("custom_fgets: ");
				return NULL; /* Some other error */
			}
		} else if (numRead == 0) {
			/* EOF */
			if (total == 0) {
				fprintf(stderr, "custom_fgets read nothing.\n");
				/* No bytes read; return null */
				return NULL;
			} else {
				/* Some bytes read; return those*/
				break;
			}
		}

		buffer[total] = singleByte;
		if (buffer[total] == '\n') {
			break;
		}
		total++;
	}
	return buffer;
}

/**
 * Parses the data from sock_fd_in into msg
 * @param msg The HTTPMsg. Must have isRequest set accordingly, and all other variables cleared.
 * @param sock_fd_in the file descriptor for the socket.
 * @return 0 on success, -1 on error, -2 on the FILE*'s EOF, -3 on unexpected blank line at start.
 */
int HTTPMsg_doParsing(HTTPMsg *msg, int sock_fd_in, bool useFirstFolderAsLoc) {
	int numObtained = 0;
	int numParsed = 0;
	int numRead = 0;
	int chunkLen = 0;
	int bufferLen = BUFFER_SIZE;
	char dummyBuffer[BUFFER_SIZE + 1];
	char buffer[BUFFER_SIZE + 1];
	char buffer2[BUFFER_SIZE + 1];
	char buffer3[BUFFER_SIZE + 1];
	msg->contentLength = CONTENT_LENGTH_NOT_SET;
	msg->state = FIRSTLINE;

	while (1) {
		//now let's get stuff from the socket!
		//first, reset the buffer. 
		memset(buffer, 0, sizeof(buffer));

		//check for timeout.
		if (!doSelecting(sock_fd_in)) {
			fprintf(stderr, "Timeout or EOF detected; exiting.\n");
			break;
		} else {
			//fprintf(stderr, "Continuing to parse the message.\n");
		}

		//not in body; use fgets 
		if (msg->state != BODY) {
			//fprintf(stderr, "NOT IN BODY. About to fgets\n");
			if (custom_fgets(buffer, BUFFER_SIZE, sock_fd_in) == NULL) {
				fprintf(stderr, "fgets null when not in body\n");
				break;
			}
			//fprintf(stderr, "NOT IN BODY. DONE WITH FGETS.\n");
			numObtained = strlen(buffer);

			//remove trailing \r\n
			if (numObtained > buffer[numObtained - 1] == '\r'
					|| buffer[numObtained - 1] == '\n') {
				buffer[numObtained - 1] = '\0';
				numObtained--;
			}
			if (buffer[numObtained - 1] == '\r'
					|| buffer[numObtained - 1] == '\n') {
				buffer[numObtained - 1] = '\0';
				numObtained--;
			}
			//fprintf(stderr, "NOT IN BODY. buffer is \"%s\"\n", buffer);
		}

		//in body but +ve content length: use fread
		else if (msg->state == BODY && msg->contentLength > 0) {
			//fprintf(stderr,
			//		"IN BODY AND CONTENT LENGTH IS > 0. FREAD COMMENCING.\n");

			numObtained = recv(sock_fd_in, buffer, bufferLen * sizeof(char),
					RECV_FLAGS);

			if (numObtained <= 0) {
			//	fprintf(stderr,
			//			"IN BODY WITH CONTENT LENGTH. FREAD EMPTY. BREAKING.\n");
				break;
			}
			//fprintf(stderr, "IN BODY WITH CONTENT LENGTH. DONE WITH FREAD.\n");
		}

		//in body; content length is chunked and chunkLen not set. Use fgets. 
		else if (msg->state == BODY && msg->contentLength == CHUNKED_LENGTH
				&& chunkLen == 0) {
			//fprintf(stderr,
			//		"CHUNKY AND IN BODY. NEED CHUNKLEN. About to fgets\n");
			if (custom_fgets(buffer, BUFFER_SIZE, sock_fd_in) == NULL) {
			//	fprintf(stderr,
			//			"CHUNKY AND IN BODY. NEED CHUNKLEN BUT FGETS EMPTY. BREAKING.\n");
				break;
			}
			//fprintf(stderr, "CHUNKY AND IN BODY. DONE WITH FGETS.\n");
			numObtained = strlen(buffer);

			//remove trailing \r\n
			if (buffer[numObtained - 1] == '\r'
					|| buffer[numObtained - 1] == '\n') {
				buffer[numObtained - 1] = '\0';
				numObtained--;
			}
			if (buffer[numObtained - 1] == '\r'
					|| buffer[numObtained - 1] == '\n') {
				buffer[numObtained - 1] = '\0';
				numObtained--;
			}
			//fprintf(stderr, "CHUNKY AND IN BODY. NEED CHUNKLEN. buffer is \"%s\"\n", buffer);
		}

		//in body with chunked content-length and chunkLen > 0. Use fread to get the exact amt of data
		//then use fgets to get rid of the newlines. 
		else if (msg->state == BODY && msg->contentLength == CHUNKED_LENGTH
				&& chunkLen > 0) {
			//fprintf(stderr,
			//		"CHUNKY AND IN BODY. HAVE CHUNKLEN. About to fread\n");

			numObtained = recv(sock_fd_in, buffer, bufferLen * sizeof(char),
					RECV_FLAGS);
			//fprintf(stderr, "CHUNKY AND IN BODY. HAVE CHUNKLEN. FREAD DONE.\n");
			if (numObtained <= 0) {
				//fprintf(stderr,
				//		"CHUNKY AND IN BODY. HAVE CHUNKLEN. OBTAINED NOTHING.\n");
				break;
			}
		}

		//we do not have content length set
		else if (msg->state
				== BODY&& msg->contentLength == CONTENT_LENGTH_NOT_SET) {fprintf(stderr, "IN BODY AND CONTENT LENGTH IS A MYSTERY. FREAD COMMENCING.\n");
		numObtained = recv(sock_fd_in, buffer, BUFFER_SIZE * sizeof(char), RECV_FLAGS);
		//fprintf(stderr, "IN BODY AND CONTENT LENGTH IS A MYSTERY. FREAD DONE.\n");
		if(numObtained <= 0) {
			//fprintf(stderr, "IN BODY AND CONTENT LENGTH IS A MYSTERY. OBTAINED NOTHING.\n");
			break;
		}
	}

	//well, this should not happen.
	else if(msg->state == BODY && msg->contentLength == 0) {
		//fprintf(stderr, "IN BODY AND CONTENT LENGTH IS ZERO. WTF??\n");
		return 0;
	}
	else {
		fprintf(stderr, "OOPS! I FAIL! state: %d, contentLength %d, chunkLen %d\n",
				msg->state, msg->contentLength, chunkLen);
		assert(false);
	}

		//now let's get to the parsing!
		if (msg->state == FIRSTLINE) {
			//fprintf(stderr, "In Firstline\n");
			//unexpected blank line at start; return custom blank line code.
			if (numObtained <= 0) {
				//fprintf(stderr, "BlankLineAtStart\n");
				continue;
			}

			//response
			else if (!msg->isRequest) {
				numParsed = sscanf(buffer, "HTTP/%f %d %[^\r\n]",
						&(msg->version), &(msg->statusCode), buffer2);
				if (numParsed < 2) {
					return -1;
				}

				//strdup to struct
				msg->reasonPhrase = strdup(buffer2);
				//fprintf(stderr,
				//		"DoParsing: FIRSTLINE complete for RESPONSE. Version %.1f, Code %d, Reason %s\n",
				//		msg->version, msg->statusCode, msg->reasonPhrase);
			}

			//request
			else {
				numParsed = sscanf(buffer, "%s %s HTTP/%f", buffer2, buffer3,
						&(msg->version));
				if (numParsed < 3) {
					return -1;
				}
				//strdup to struct
				msg->method = strdup(buffer2);
				msg->resource = strdup(buffer3);
				//fprintf(stderr,
				//		"DoParsing: FIRSTLINE complete for REQUEST. Version %.1f, Resource %s, Method %s\n",
				//		msg->version, msg->resource, msg->method);
			}
			msg->state = HEADERS;
		}

		//head
		else if (msg->state == HEADERS) {
			//fprintf(stderr, "In headers\n");
			//blank line: go to body
			if (numObtained <= 0) {
				msg->state = BODY;
				if (msg->isRequest
						&& msg->contentLength == CONTENT_LENGTH_NOT_SET) {
					msg->state = END;
					break;
				} else if (!msg->isRequest && !isBodyOk(msg->statusCode)) {
					msg->state = END;
					break;
				}

				//non-chunked: decrement buffer size as needed. 
				if (msg->contentLength > 0) {
					bufferLen = MIN(BUFFER_SIZE, msg->contentLength);
				}

				//fprintf(stderr, "doParsing: HEADERS state complete.\n");
			}

			//space; add to prev
			else if (isspace(buffer[0])) {
				char *iter = buffer;

				//go to first nonwhitespace char?
				while (isspace(*iter))
					iter++;

				//add to previous header
				HeaderList_addToLastHeader(msg->headerList, iter);
			}
			//we do not have space
			else {
				memset(buffer2, 0, sizeof(buffer2));
				memset(buffer3, 0, sizeof(buffer3));
				sscanf(buffer, "%[^ :] %[:] %[^\r\n]", buffer2, dummyBuffer,
						buffer3);

				HeaderList_addHeader(msg->headerList, buffer2, buffer3);
				//check for content length
				if (strcasecmp(buffer2, "Content-Length") == 0) {
					sscanf(buffer3, "%d", &(msg->contentLength));
				}

				if (strcasecmp(buffer2, "Transfer-Encoding") == 0) {
					//this means "chunk" unless "identity"
					if (strcasestr(buffer3, "identity") == NULL) {
						msg->contentLength = CHUNKED_LENGTH;
						//printf("We are chunking!\n");
					}
				}
			}
			//fprintf(stderr,
			//		"doParsing: current tail header: \"%s\", value \"%s\"\n",
			//		msg->headerList->tail->name, msg->headerList->tail->value);
		} else if (msg->state == BODY) {
			//fprintf(stderr, "In body\n");
			//we have a body; and a content length too!
			if (msg->contentLength > 0) {
				//add to msg data
				if (msg->fileptr == NULL) {
					msg->fileptr = tmpfile();
				}
				////
				fwrite(buffer, sizeof(char), numObtained, msg->fileptr);
				fflush(msg->fileptr);

				//NOTE: Caller is responsible for closing msg->fileptr.
				numRead += numObtained;
				bufferLen = MIN(BUFFER_SIZE, msg->contentLength - numRead);
				if (numRead >= msg->contentLength) {
					msg->state = END;
					break;
				}
			}
			//chunky: go until 0 len chunk
			else if (msg->contentLength == CHUNKED_LENGTH) {
				if (chunkLen == 0) {
					unsigned int scanlen = 0;
					//printf("about to set chunklen! buffer \"%s\"\n", buffer);
					sscanf(buffer, "%x", &scanlen);
					chunkLen = (int) scanlen;
					//printf("We have chunk of %d bytes\n", chunkLen);
					if (chunkLen <= 0) {
						msg->state = TRAILERS;
					}
					bufferLen = MIN(BUFFER_SIZE, chunkLen);
				} else {
					//add to msg data
					if (msg->fileptr == NULL) {
						msg->fileptr = tmpfile();
					}
					////
					fwrite(buffer, sizeof(char), numObtained, msg->fileptr);
					fflush(msg->fileptr);

					//subtract numObtained from chunklen. 		
					chunkLen -= numObtained;
					//printf("Read %d bytes. Wrote %d bytes. Rest of chunk is %d bytes\n", numObtained, numWrote, chunkLen);

					//if chunklen is zero, do a fgets into dummy buffer to flush out \r\n					
					if (chunkLen <= 0) {
						chunkLen = 0;
						custom_fgets(dummyBuffer, BUFFER_SIZE, sock_fd_in);
					} else {
						bufferLen = MIN(BUFFER_SIZE, chunkLen);
					}
				}
			}

			//no idea of length; keep going til eof
			else {
				//add to msg data
				if (msg->fileptr == NULL) {
					msg->fileptr = tmpfile();
				}
				////
				fwrite(buffer, sizeof(char), numObtained, msg->fileptr);
				fflush(msg->fileptr);
			}
		}

		else if (msg->state == TRAILERS) {
			//fprintf(stderr, "trailers\n");
			//blank line: go to end
			if (numObtained <= 0) {
				msg->state = END;
				break;
			}

			//space; add to prev
			else if (isspace(buffer[0])) {
				char *iter = buffer;

				//go to first nonwhitespace char?
				while (isspace(*iter))
					iter++;

				//add to previous header
				HeaderList_addToLastHeader(msg->trailerList, iter);
			}
			//we do not have space
			else {
				memset(buffer2, 0, sizeof(buffer2));
				memset(buffer3, 0, sizeof(buffer3));
				sscanf(buffer, "%[^ :] %[:] %[^\r\n]", buffer2, dummyBuffer,
						buffer3);

				HeaderList_addHeader(msg->trailerList, buffer2, buffer3);
			}
			break;
		}
		//additional states here
		else {
			//fprintf(stderr, "other state\n");
			break;
		}
	}

	//fprintf(stderr, "doParsing: about to load host, path, & port\n");
	HTTPMsg_loadHostPathAndPort(msg, useFirstFolderAsLoc);
	return 0;
}
/******************************************************************************/
/*                           END HTTP STUFF                                   */
/******************************************************************************/
